package sso

import (
	"SSO/config"
	"SSO/controllers"
	"SSO/models"
	"SSO/tools"
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"net/url"
	"time"
)

type SSO struct {
	controllers.Controller
}

func (this SSO) LoginView(c *gin.Context){

	urlStr := c.DefaultQuery("callback_url", "")
	if urlStr == "" {
		this.ReturnJson(-1, "URL参数为空", nil, c)
		return
	}

	urlObj, err := url.Parse(urlStr)
	if err != nil {
		this.ReturnJson(-1, err.Error(), nil, c)
		return
	}

	domain := fmt.Sprintf("%s://%s", urlObj.Scheme, urlObj.Host)
	username := this.SessionGet("username", c)

	if username == nil {
		c.HTML(http.StatusOK, "sso/login.html", nil)
		return
	}

	if domain == config.GetConf().Base.Domain {
		c.Redirect(http.StatusFound, urlObj.Path)
		return
	}

	service, err := models.Services{}.GetByDomain(domain)
	if err != nil {
		this.ReturnJson(-1, err.Error(), nil, c)
		return
	}

	if service == nil {
		this.ReturnJson(-1, fmt.Sprintf("该URL[%s]未接入SSO系统", urlStr), nil, c)
		return
	}

	uid := tools.Function{}.GetStrMd5(fmt.Sprintf("%s:%d:%s", service.SecretKey, time.Now().UnixNano(), username.(string)))
	user, _ := models.Users{}.GetByUsername(username.(string))
	if user == nil {
		this.ReturnJson(-1, fmt.Sprintf("登录用户[%s]不存在", username), nil, c)
		return
	}

	sessionId, _ := this.SessionIdGet(c)

	sessionIdMd5 := tools.Function{}.GetStrMd5(sessionId)

	err = models.Logins{}.Create(sessionIdMd5, user.Id, service.Id, uid, 0, (time.Now().Unix() + 86400))
	if err != nil {
		this.ReturnJson(-1, "uid分配失败 "+err.Error(), nil, c)
		return
	}

	serviceLoginUrl := fmt.Sprintf("%s%s?uid=%s&url=%s", service.Domain, service.PathLogin, uid, urlStr)

	c.Redirect(http.StatusFound, serviceLoginUrl)
}

func (this SSO) Login(c *gin.Context){
	username := c.PostForm("username")
	password := c.PostForm("password")

	passwordMd5 := tools.Function{}.GetStrMd5(password)
	user, err := models.Users{}.GetByUsernameAndPassword(username, passwordMd5)

	if err != nil {
		this.ReturnJson(-1, err.Error(), nil, c)
		return
	}

	if user == nil {
		this.ReturnJson(-1, "用户名或密码错误", nil, c)
		return
	}

	err = this.SessionSet("username", username, c)
	if err != nil {
		this.ReturnJson(-1, "登录失败", nil, c)
		return
	}

	this.ReturnJson(0, "登录成功", nil, c)

}

func (this SSO) VerifyUid(c *gin.Context) {
	uid := c.PostForm("uid")

	login, err := models.Logins{}.GetByUid(uid)
	if err != nil {
		this.ReturnJson(-1, err.Error(), nil, c)
		return
	}
	if login == nil {
		this.ReturnJson(-1, "uid不正确", nil, c)
		return
	}

	if login.IsVerify == 1 {
		this.ReturnJson(-1, "uid已被使用", nil, c)
		return
	}

	err = models.Logins{}.UpdateIsVerifyByUid(uid, 1)
	if err != nil {
		this.ReturnJson(-1, "uid验证失败", nil, c)
		return
	}

	user, err := models.Users{}.GetById(login.UserId)

	if err != nil {
		this.ReturnJson(-1, "uid验证失败", nil, c)
		return
	}

	this.ReturnJson(0, "success", map[string]interface{}{
		"username": user.Username,
	}, c)
}

func (this SSO) Logout(c *gin.Context) {
	sessionId, _ := this.SessionIdGet(c)
	sessionIdMd5 := tools.Function{}.GetStrMd5(sessionId)
	logins, err := models.Logins{}.GetBySessionId(sessionIdMd5)

	if err != nil {
		this.ReturnJson(-1, fmt.Sprintf("SSO查询登录信息失败 err[%s]", err.Error()), nil, c)
		return
	}

	serviceIdMap 	:= make(map[int]struct{}, 0)
	serviceIds 		:= make([]int, 0)
	serviceLogout 	:= make([]string, 0)

	for _, login := range logins {
		if login.IsVerify == 1 {
			serviceIdMap[login.ServiceId] = struct{}{}
		}
	}

	for serviceId := range serviceIdMap {
		serviceIds = append(serviceIds, serviceId)
	}

	services, err := models.Services{}.GetByIds(serviceIds)

	if err != nil {
		this.ReturnJson(-1, fmt.Sprintf("SSO查询服务信息失败 info[%s]", err.Error()), nil, c)
		return
	}

	for _, service := range services {
		if service.Domain == config.GetConf().Base.Domain {
			continue
		}
		serviceLogout = append(serviceLogout, fmt.Sprintf("%s%s", service.Domain, service.PathLogout))
	}

	c.HTML(http.StatusOK, "sso/logout.html", gin.H{"data": serviceLogout})
}

func (this SSO) DeleteLogin(c *gin.Context)  {

	err := this.SessionDelete("username", c)

	if err != nil {
		this.ReturnJson(-1, fmt.Sprintf("SSO清除登录信息失败 info[%s]", err.Error()), nil, c)
		return
	}

	sessionId, _ := this.SessionIdGet(c)
	sessionIdMd5 := tools.Function{}.GetStrMd5(sessionId)
	_ = models.Logins{}.DeleteBySessionId(sessionIdMd5)

	this.ReturnJson(0, "success", nil, c)
}

func (this SSO) ClearExpireLogin()  {
	err := models.Logins{}.ClearExpire()
	if err != nil {
		fmt.Println(err)
	}
}